Skip to content

fix(extensions): make mandatory-hook dispatch contract self-contained#2901

Merged
mnriem merged 1 commit into
github:mainfrom
Quratulain-bilal:fix/hook-directive-dispatch-contract
Jun 25, 2026
Merged

fix(extensions): make mandatory-hook dispatch contract self-contained#2901
mnriem merged 1 commit into
github:mainfrom
Quratulain-bilal:fix/hook-directive-dispatch-contract

Conversation

@Quratulain-bilal

@Quratulain-bilal Quratulain-bilal commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #2730

Problem

The EXECUTE_COMMAND: directive emitted for optional: false hooks is meant as a dispatch signal. In agent-direct invocations (a slash command run inside Claude Code, Cursor, Codex, etc. - the default pattern for extension users), nothing watches for it. The agent emits the directive, waits, and moves on - the hook never runs, and the failure is completely silent.

I could not find any code path in this repository that consumes the directive: format_hook_message()'s only caller is check_hooks_for_event(), which itself has no production callers. So in practice the bundled command templates are what drive hook emission - the agent reads .specify/extensions.yml and emits the block by following the template instructions. That makes the templates the right place to fix this.

Changes

  • templates/commands/*.md (all 10 command templates): added one instruction line after each mandatory-hook block (pre- and post-execution, 20 sites). It tells the agent that emitting the block alone does not run the hook - it must actually invoke the hook and wait for completion before continuing.
  • The instruction deliberately does not hardcode /{command}. It tells the agent to run the hook the way it would run the command itself in the current agent/session, and notes the invocation may differ from the literal {command} id shown in the block (e.g. skills-mode agents run it as /skill:speckit-... or $speckit-...). This keeps it correct outside the default slash-command form.

Scope

This is the minimal version of an earlier, broader change, per maintainer feedback to keep the surface small. The example blocks are left untouched; only a single instruction line is added per mandatory-hook block. No runtime code, API reference, or tests are changed - the fix is entirely in the bundled template instructions the agent follows.

AI disclosure

This PR was authored with AI assistance (Claude Code); the change and this description are under my direction and review.

@Quratulain-bilal Quratulain-bilal requested a review from mnriem as a code owner June 9, 2026 03:21
@Quratulain-bilal Quratulain-bilal changed the title fix(extensions): make mandatory-hook dispatch contract self-contained… fix(extensions): make mandatory-hook dispatch contract self-contained Jun 9, 2026

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution. A couple of questions before we can move forward:

  1. Orchestrator claim: The PR body references specify run and "CLI orchestrators that watch agent output" as existing consumers of the EXECUTE_COMMAND: directive. Can you point to the code path where this dispatch actually happens today? If no orchestrator currently consumes the directive, the backward-compatibility framing is misleading and the PR description should be corrected.

  2. AI disclosure: Was this PR authored with AI assistance? If so, please disclose per contributing guidelines.

The underlying fix (agent self-dispatches mandatory hooks in agent-direct sessions) addresses a real problem, but the bar is high for sweeping template changes and we need confidence the framing accurately reflects the codebase.

@Quratulain-bilal

Quratulain-bilal commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

closed this by accident while editing the description - reopened.

on the orchestrator question: you're right, I rechecked and couldn't find anything in the repo that actually consumes
EXECUTE_COMMAND today. execute_hook() has no production callers and auto_execute_hooks gets written as a default but
never read. I picked that framing up from the issue author's wording in #2730 without verifying it myself. fixed the PR
description.

and yes, this was built with AI assistance (Claude Code) - code, tests and description, reviewed by me. added the
disclosure to the PR body.

if the template changes are too broad I can cut it down to just the format_hook_message() change + tests and do the
templates as a follow-up.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens the extension mandatory-hook “dispatch contract” so agent-direct invocations (Claude Code / Cursor / Codex, etc.) don’t silently emit EXECUTE_COMMAND: without actually running the hook, by explicitly instructing the agent to self-dispatch and wait.

Changes:

  • Updates runtime hook messaging to explain that EXECUTE_COMMAND: is a signal (not a dispatcher) and that mandatory hooks must be invoked and awaited.
  • Updates all bundled command templates to embed the same dispatch-contract guidance and tightens “Done When” criteria.
  • Adds/extends tests to prevent regressions in both runtime messages and bundled templates.
Show a summary per file
File Description
src/specify_cli/extensions.py Extends mandatory-hook message text to instruct self-dispatch.
templates/commands/analyze.md Adds dispatch-contract bullet after mandatory hook blocks.
templates/commands/checklist.md Adds dispatch-contract bullet after mandatory hook blocks.
templates/commands/clarify.md Adds dispatch-contract bullet after mandatory hook blocks and tightens Done When.
templates/commands/constitution.md Adds dispatch-contract bullet after mandatory hook blocks.
templates/commands/implement.md Adds dispatch-contract bullet after mandatory hook blocks and tightens Done When.
templates/commands/plan.md Adds dispatch-contract bullet after mandatory hook blocks and tightens Done When.
templates/commands/specify.md Adds dispatch-contract bullet after mandatory hook blocks and tightens Done When.
templates/commands/tasks.md Adds dispatch-contract bullet after mandatory hook blocks and tightens Done When.
templates/commands/taskstoissues.md Adds dispatch-contract bullet after mandatory hook blocks.
extensions/EXTENSION-API-REFERENCE.md Documents the dispatch contract for extension authors.
tests/test_extensions.py Adds tests for the runtime message contract and template coverage.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 12/12 changed files
  • Comments generated: 19

Comment thread src/specify_cli/extensions.py Outdated
Comment thread templates/commands/analyze.md Outdated
Comment thread templates/commands/checklist.md Outdated
Comment thread templates/commands/clarify.md Outdated
Comment thread templates/commands/constitution.md Outdated
Comment thread templates/commands/implement.md Outdated
Comment thread templates/commands/plan.md Outdated
Comment thread templates/commands/specify.md Outdated
Comment thread templates/commands/tasks.md Outdated
Comment thread templates/commands/taskstoissues.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 12/12 changed files
  • Comments generated: 20

Comment thread templates/commands/tasks.md Outdated
Comment thread templates/commands/tasks.md Outdated
Comment thread templates/commands/specify.md Outdated
Comment thread templates/commands/specify.md Outdated
Comment thread templates/commands/plan.md Outdated
Comment thread templates/commands/constitution.md Outdated
Comment thread templates/commands/taskstoissues.md Outdated
Comment thread templates/commands/taskstoissues.md Outdated
Comment thread extensions/EXTENSION-API-REFERENCE.md Outdated
Comment thread tests/test_extensions.py Outdated
@mnriem

mnriem commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 12/12 changed files
  • Comments generated: 0 new

@mnriem

mnriem commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Yes, the template changes are way too broad. We should look into a fix with a smaller surface

@Quratulain-bilal

Copy link
Copy Markdown
Contributor Author

makes sense, agree it's too broad.

while trying to shrink it I found the reason it spread: format_hook_message() in extensions.py isn't actually reached at runtime its only caller is check_hooks_for_event(), which nothing in the CLI or agent path calls. the hook block is emitted by the agent following the command-template instructions (it reads .specify/extensions.yml and outputs the block itself), so the templates are where the behavior actually lives hence all 9.

two smaller options:
one line in the mandatory-hook instruction of each template, no example-block edits - still 9 files but a tiny diff each
or fix just plan as the pattern, then the rest in a follow-up

any preference on direction?

@mnriem

mnriem commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Pick one and lets see how it looks

@Quratulain-bilal Quratulain-bilal force-pushed the fix/hook-directive-dispatch-contract branch from 5339123 to aabc7c7 Compare June 17, 2026 05:48
@Quratulain-bilal

Copy link
Copy Markdown
Contributor Author

went with option 1 - one instruction line after each mandatory-hook block, example blocks untouched. rebased on current main so the diff is clean: 9 files, 18 lines.

@mnriem mnriem self-requested a review June 17, 2026 12:31
@Quratulain-bilal

Copy link
Copy Markdown
Contributor Author

@mnriem this is ready for another look when you have time - went with the minimal option (one line per template, examples untouched, 9 files / 18 lines). no rush, just flagging it's been sitting since the rebase.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 9/9 changed files
  • Comments generated: 18

Comment thread templates/commands/analyze.md Outdated
Comment thread templates/commands/analyze.md Outdated
Comment thread templates/commands/checklist.md Outdated
Comment thread templates/commands/checklist.md Outdated
Comment thread templates/commands/tasks.md Outdated
Comment thread templates/commands/clarify.md Outdated
Comment thread templates/commands/constitution.md Outdated
Comment thread templates/commands/constitution.md Outdated
Comment thread templates/commands/taskstoissues.md Outdated
Comment thread templates/commands/taskstoissues.md Outdated
@Quratulain-bilal Quratulain-bilal force-pushed the fix/hook-directive-dispatch-contract branch from aabc7c7 to 5ec7882 Compare June 24, 2026 20:22
@mnriem mnriem requested a review from Copilot June 24, 2026 20:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 10/10 changed files
  • Comments generated: 11

Comment thread templates/commands/analyze.md Outdated
Comment thread templates/commands/checklist.md Outdated
Comment thread templates/commands/clarify.md Outdated
Comment thread templates/commands/constitution.md Outdated
Comment thread templates/commands/converge.md Outdated
Comment thread templates/commands/plan.md Outdated
Comment thread templates/commands/specify.md Outdated
Comment thread templates/commands/tasks.md Outdated
Comment thread templates/commands/taskstoissues.md Outdated
Comment thread templates/commands/plan.md Outdated
@mnriem

mnriem commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback

… directive

In agent-direct invocations nothing watches agent output for the
EXECUTE_COMMAND: directive, so a mandatory hook that is only emitted
never runs and the failure is silent (github#2730). Add one line after each
mandatory-hook block instructing the agent to actually invoke the hook
and wait for it before continuing.

The instruction tells the agent to run the hook the way it would run
the command itself in the current agent/session, and notes the
invocation may differ from the literal {command} id shown in the block
(e.g. skills-mode agents run it as /skill:speckit-... or $speckit-...),
so it stays correct outside the default slash-command form.

Fixes github#2730
@Quratulain-bilal Quratulain-bilal force-pushed the fix/hook-directive-dispatch-contract branch from 5ec7882 to 07faeef Compare June 25, 2026 11:39
@Quratulain-bilal

Copy link
Copy Markdown
Contributor Author

addressed both Copilot points:

  • the dispatch line no longer leans on the literal /{command} id. it now tells the agent to run the hook the way it'd run the command itself in this agent/session, and calls out that the invocation can differ from the {command} id (e.g. /skill:speckit-... or $speckit-... in skills mode). so the id-vs-invocation distinction is explicit now.
  • fixed the PR description - it was stale from the earlier broad version and claimed runtime/API-ref/test changes that aren't in this minimal cut. the change is now purely the template instruction lines, and the description says exactly that.

also rebased on current main, diff is 10 files / 20 lines.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 10/10 changed files
  • Comments generated: 0 new

@mnriem mnriem merged commit bb37b18 into github:main Jun 25, 2026
12 checks passed
@mnriem

mnriem commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Thank you!

HeroSizy pushed a commit to HeroSizy/spec-kit that referenced this pull request Jun 25, 2026
… directive (github#2901)

In agent-direct invocations nothing watches agent output for the
EXECUTE_COMMAND: directive, so a mandatory hook that is only emitted
never runs and the failure is silent (github#2730). Add one line after each
mandatory-hook block instructing the agent to actually invoke the hook
and wait for it before continuing.

The instruction tells the agent to run the hook the way it would run
the command itself in the current agent/session, and notes the
invocation may differ from the literal {command} id shown in the block
(e.g. skills-mode agents run it as /skill:speckit-... or $speckit-...),
so it stays correct outside the default slash-command form.

Fixes github#2730
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: PR #2713's EXECUTE_COMMAND directive emits correctly but is never executed in pure-Claude-Code invocations

3 participants